<?php
/**
 * This is Lil helper. 
 *
 * @copyright     Copyright 2008-2012, LilCake.net
 * @link          http://lilcake.net LilCake.net
 * @package       lil
 * @subpackage    lil.views.helpers
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
App::uses('AppHelper', 'View/Helper');
/**
 * LilHelper class
 *
 * @uses          Helper
 * @package       lil
 * @subpackage    lil.views.helpers
 */
class LilHelper extends AppHelper {
/**
 * helpers property
 *
 * @var array
 */
	public $helpers = array('Html', 'Form', 'Paginator', 'LilFloat.LilFloat');
/**
 * __jsReady property
 *
 * @var array
 */
	public $__jsReady = array();
/**
 * __popups property
 *
 * @var array
 */
	public $__popups = array();
/**
 * Default Constructor
 *
 * @param View $View The View this helper is being attached to.
 * @param array $settings Configuration settings for the helper.
 */
	public function __construct(View $View, $settings = array()) {
		parent::__construct($View);
	
		// set current user
		App::uses('LilCurrentUser', 'Lil.Lib');
		$this->currentUser = LilCurrentUser::getInstance();
		
		// set current area
		App::uses('LilCurrentArea', 'Lil.Lib');
		$this->currentArea = LilCurrentArea::getInstance();
	}
/**
 * __call method
 *
 * @param mixed $method
 * @param mixed $params
 * @access private
 * @return void
 */
	function __call($method, $params) {
		if (!empty($params[0]) && ($model = ClassRegistry::Init($params[0]))) {
			if (is_callable(array($model, $method))) {
				unset($params[0]);
				return (call_user_func_array(array($model, $method), $params));
			}
		}
		return false;
	}
/**
 * jsReady method
 *
 * @param mixed $block
 * @access public
 * @return void
 */
	function jsReady($block) {
		$this->__jsReady[] = $block;
	}
/**
 * jsReadyOut method
 *
 * @access public
 * @return string
 */
	function jsReadyOut() {
		return implode(PHP_EOL, $this->__jsReady);
	}
/**
 * referer method
 *
 * @access public
 * @return string
 */
	function referer() {
		return base64_encode($this->request->referer());
	}
/**
 *  link method
 * 
 * Creates a HTML link. Behaves exactly like Html::link with ability to use 
 * nicer links in form like "[Link] additional data"
 *
 * @param string $title The content to be wrapped by <a> tags.
 * @param mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
 * @param array $options Array of HTML attributes.
 * @param string $confirmMessage JavaScript confirmation message.
 * @return string An <a /> element.
 * @access public
 */
	function link(/*$title, $url = null, $options = array(), $confirmMessage = false*/) {
		$params = func_get_args();
		
		if (preg_match_all('/\[(\$(\d))?([^\]]+)\]/i', $params[0], $matches)) {
			$ret = $params[0];
			foreach ($matches[0] as $k => $match) {
				$link = $this->Html->link(
					$matches[3][$k],
					isset($params[$matches[2][$k]][0]) ? $params[$matches[2][$k]][0] : null,
					isset($params[$matches[2][$k]][1]) ? $params[$matches[2][$k]][1] : null,
					isset($params[$matches[2][$k]][2]) ? $params[$matches[2][$k]][2] : null
				);
				$ret = str_replace($match, $link, $ret);
			}
			return $ret;
		} else {
			return $this->Html->link(
				$params[0],
				isset($params[1]) ? $params[1] : null,
				isset($params[2]) ? $params[2] : null,
				isset($params[3]) ? $params[3] : null
			);
		}
	}
/**
 * deleteLink method
 *
 * Returns default delete link
 *
 * @param mixed $url_options Either an array with url or model's id
 * @access public
 * @return mixed
 */
	function deleteLink($url_options = array(), $link_options = array(), $image_options = array()) {
		$url_defaults = array(
			'action' => 'delete'
		);
		
		return $this->Html->link(
			$this->Html->image('/lil/img/delete.gif', $image_options),
			array_merge($url_defaults, (array)$url_options),
			array_merge(
				array(
					'escape' => false,
					'confirm' => __d('lil', 'Are you sure you want to delete this record?', true)
				),
				$link_options
			)
		);
	}
/**
 * editLink method
 *
 * Returns default edit link
 *
 * @param mixed $url_options Either an array with url or model's id
 * @param mixed $link_options Array with options applied to link element
 * @param mixed $image_options Array with options applied to image element
 * @access public
 * @return mixed
 */
	function editLink($url_options = array(), $link_options = array(), $image_options = array()) {
		$url_defaults = array(
			'action' => 'edit'
		);
		
		return $this->Html->link(
			$this->Html->image('/lil/img/edit.gif', $image_options),
			array_merge($url_defaults, (array)$url_options),
			array_merge(
				array(
					'escape' => false,
				),
				$link_options
			)
		);
	}
/**
 * viewLink method
 *
 * Returns default view link
 *
 * @param mixed $url_options Either an array with url or model's id
 * @param mixed $link_options Array with options applied to link element
 * @param mixed $image_options Array with options applied to image element
 * @access public
 * @return mixed
 */
	function viewLink($url_options = array(), $link_options = array(), $image_options = array()) {
		$url_defaults = array(
			'action' => 'view'
		);
		
		return $this->Html->link(
			$this->Html->image('/lil/img/view.gif', $image_options),
			array_merge($url_defaults, (array)$url_options),
			array_merge(
				array(
					'escape' => false,
				),
				$link_options
			)
		);
	}
/**
 * Replaces double line-breaks with paragraph elements.
 *
 * A group of regex replaces used to identify text formatted with newlines and
 * replace double line-breaks with HTML paragraph tags. The remaining
 * line-breaks after conversion become <<br />> tags, unless $br is set to '0'
 * or 'false'.
 *
 * @since 0.71
 *
 * @param string $pee The text which has to be formatted.
 * @param int|bool $br Optional. If set, this will convert all remaining line-breaks after paragraphing. Default true.
 * @return string Text which has been converted into correct paragraph tags.
 */
	public function autop($pee, $br = 1) {
		if ( trim($pee) === '' )
			return '';
		$pee = $pee . "\n"; // just to make things a little easier, pad the end
		$pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
		// Space things out a little
		$allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
		$pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
		$pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
		$pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
		if ( strpos($pee, '<object') !== false ) {
			$pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed
			$pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee);
		}
		$pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
		// make paragraphs, including one at the end
		$pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY);
		$pee = '';
		foreach ( $pees as $tinkle )
			$pee .= '<p>' . trim($tinkle, "\n") . "</p>\n";
		$pee = preg_replace('|<p>\s*</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
		$pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
		$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag
		$pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists
		$pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
		$pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
		$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
		$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
		if ($br) {
			$pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', create_function('$matches', 'return str_replace("\n", "<PreserveNewline />", $matches[0]);'), $pee);
			$pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks
			$pee = str_replace('<PreserveNewline />', "\n", $pee);
		}
		$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
		$pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
		if (strpos($pee, '<pre') !== false)
			$pee = preg_replace_callback('!(<pre[^>]*>)(.*?)</pre>!is', 'clean_pre', $pee );
		$pee = preg_replace( "|\n</p>$|", '</p>', $pee );
		//$pee = preg_replace('/<p>\s*?(' . get_shortcode_regex() . ')\s*<\/p>/s', '$1', $pee); // don't auto-p wrap shortcodes that stand alone

		return $pee;
	}
/**
 * insertIntoArray method
 *
 * Insert a new element into array
 *
 * @param array $dest
 * @param array $element
 * @param array $options
 * @access public
 * @return void
 */
	function insertIntoArray(&$dest, $element, $options) {
		if (isset($options['after']) || isset($options['replace'])) {
			$title = isset($options['after']) ? $options['after'] : $options['replace'];
			
			$panels = array_keys($dest);
			$i = 0;
			for ($i = 0; $i < sizeof($dest); $i++) {
				if ($panels[$i] === $title) break;
			}
			
			if (isset($panels[$i]) && $panels[$i] == $title) {
				if (isset($options['replace'])) { unset($dest[$title]); $i--; };
				
				if (isset($options['preserve']) && $options['preserve'] === false) {
					$part1 = array_slice($dest, 0, $i + 1, true);
					foreach ($element as $elk => $elv) {
						 if (is_numeric($elk)) {
						 	$part1[] = $elv;
						 } else {
						 	$part1[$elk] = $elv;
						 }
					}
					$dest = array_merge($part1, array_slice($dest, $i + 1, sizeof($dest) - $i, true));
				} else {
					// do this to preserve array keys
					$dest = 
						array_slice($dest, 0, $i + 1, true) + 
						$element + 
						array_slice($dest, $i + 1, sizeof($dest) - $i, true);
				}
			}
		} else if (isset($options['before'])) {
			$panels = array_keys($dest);
			$i = 0;
			for ($i = 0; $i < sizeof($dest); $i++) {
				if ($panels[$i] === $options['before']) break;
			}
			
			if ($panels[$i] == $options['before']) {
				if (isset($options['preserve']) && $options['preserve'] === false) {
					$part1 = array_slice($dest, 0, $i, true);
					
					foreach ($element as $elk => $elv) {
						 if (is_numeric($elk)) {
						 	$part1[] = $elv;
						 } else {
						 	$part1[$elk] = $elv;
						 }
					}
					
					$dest = array_merge($part1, array_slice($dest, $i, sizeof($dest) - $i, true));
				} else {
					// do this to preserve array keys
					$dest = 
						array_slice($dest, 0, $i, true) + 
						$element + 
						array_slice($dest, $i, sizeof($dest) - $i, true);
				}
			}
		} else if (isset($options['end'])) {
			$dest = $dest + $element;
		}
	}
/**
 * menu method
 *
 * Display main menu from LilArray
 *
 * @param mixed $data Menu compliant with LilMenu specifications
 * @return void
 */
	public function menu($data) {
		print('<ul>');
		foreach ($data as $menu_item_name => $menu_item) {
			if (!empty($menu_item['visible'])) {
				$params = array();
				if (!empty($menu_item['submenu'])) {
					$params['class']  = 'popup_link';
					$params['id']     = 'popup_' . $menu_item_name;
				}
				if (!empty($menu_item['style'])) $params['style'] = $menu_item['style'];
				if (!empty($menu_item['id']) && empty($params['id'])) $params['id'] = $menu_item['id'];
				
				$params_str = "";
				foreach ($params as $p_k => $p_v) {
					$params_str .= ' ' . $p_k . '="' . $p_v . '"';
				}
				
				printf('<li%2$s>%1$s</li>',
					$this->Html->link(
						$menu_item['title'] . (empty($menu_item['submenu']) ? '' : ' ▼'),
						empty($menu_item['url']) ? '#' : $menu_item['url'],
						isset($menu_item['params']) ? $menu_item['params'] : null
					),
					$params_str
				);
				
				if (!empty($menu_item['submenu'])) {
					$popup_data = array();
					
					foreach ($menu_item['submenu'] as $submenu_item) {
						$popup_data['items'][] = array(
							'title' => $submenu_item['title'],
							'url' => empty($submenu_item['url']) ? '#' : $submenu_item['url'],
							'params' => isset($submenu_item['params']) ? $submenu_item['params'] : null
						);
					}
					
					$this->popup($menu_item_name, $popup_data);
				}
			}
		}
		print('</ul>');
	}
/**
 * form method
 *
 * Display form from LilArray
 *
 * @param mixed $data Form compliant with LilForm specifications
 * @return void
 */
	public function popup($name, $data, $inline = false) {
		$ret = sprintf('<div class="popup_%s popup ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"><ul>', $name);
		foreach($data['items'] as $item) {
			$ret .= sprintf('<li>%1$s</li>',
				$this->Html->link(
					$item['title'],
					empty($item['url']) ? '#' : $item['url'],
					isset($item['params']) ? $item['params'] : null
				)
			);
		}
		$ret .= '</ul></div>';
		
		$this->__popups[$name] = $ret;
	}
	
	public function outputPopups() {
		foreach ($this->__popups as $popup_name => $popup_str) {
			echo $popup_str;
		}
	}
/**
 * form method
 *
 * Display form from LilArray
 *
 * @param mixed $data Form compliant with LilForm specifications
 * @return void
 */
	public function form($data) {
		// display title
		$this->_View->set('title_for_layout', $data['title_for_layout']);
		
		// display menu
		if (!empty($data['menu'])) {
			$this->_View->set('main_menu', $data['menu']);
		}
		
		// form display begins
		if (is_string($data['form']['pre'])) {
			echo $data['form']['pre']; 
		} else {
			foreach ($data['form']['pre'] as $line) {
				echo $line;
			}
			
		}
		foreach ($data['form']['lines'] as $name => $line) {
			if (is_string($line)) {
				echo $line;
			} else if (!empty($line['class'])) {
				$parameters = array();
				if (!empty($line['parameters'])) {
					$parameters = (array)$line['parameters'];
				}
				if (is_object($line['class'])) {
					$use_object =& $line['class'];
				} else {
					$use_object =& ${$line['class']};
				}
				echo call_user_func_array(array($use_object, $line['method']), $parameters);
			}
		}
		if (is_string($data['form']['post'])) {
			echo $data['form']['post']; 
		} else {
			foreach ($data['form']['post'] as $line) {
				echo $line;
			}
		}
	}
/**
 * index method
 *
 * Display index from LilArray
 *
 * @param mixed $data Table compliant with LilIndex specifications
 * @return void
 */
	public function index($data) {
		// display title
		if (isset($data['title_for_layout'])) {
			$this->_View->set('title_for_layout', $data['title_for_layout']);
		}
		if (isset($data['head_for_layout'])) {
			$this->_View->set('head_for_layout', $data['head_for_layout']);
		}
		
		// display menu
		if (!empty($data['menu'])) $this->_View->set('main_menu', $data['menu']);
		
		// display actions
		if (!empty($data['actions'])) $this->_actions($data['actions']);
		
		if (empty($data['table']['element']['body']['rows'])) {
			printf('<p>%s</p>', __d('lil', 'No records found.'));
		} else {
			
			echo $this->_table($data['table']);
		}
	}
/**
 * panels method
 *
 * Display panels from LilArray
 *
 * @param mixed $data View compliant with LilIndex specifications
 * @return void
 */
	public function panels($data) {
		// display title
		if (isset($data['title_for_layout'])) {
			$this->_View->set('title_for_layout', $data['title_for_layout']);
		}
		if (isset($data['head_for_layout'])) {
			$this->_View->set('head_for_layout', $data['head_for_layout']);
		}
		
		// display menu
		if (!empty($data['menu'])) $this->_View->set('main_menu', $data['menu']);
		
		// actions
		if (!empty($data['actions'])) $this->_actions($data['actions']);
		
		foreach ($data['panels'] as $panel) {
			if (is_array($panel)) {
				if (!empty($panel['pre'])) echo $panel['pre'];
				
				$params = array('class' => 'view-panel');
				if (isset($panel['id'])) $params['id'] = $panel['id'];
				
				if (isset($panel['params']['class']) && is_string($panel['params']['class'])) {
					$params['class'] .= $panel['params']['class'];
					unset($panel['params']['class']);
				}
				
				if (!empty($panel['params']) && is_array($panel['params'])) {
					$params = array_merge($params, (array)$panel['params']);
				}
				
				$params_str = "";
				foreach ($params as $p_k => $p_v) {
					$params_str .= ' ' . $p_k . '="' . $p_v . '"';
				}
				
				printf('<div%1$s>', $params_str);
				
				if (isset($panel['lines']) && is_array($panel['lines'])) {
					foreach ($panel['lines'] as $line) {
						if (is_array($line)) {
							if (isset($line['label'])) printf('<span class="label">%s</span>', $line['label']);
							if (isset($line['text'])) print($line['text']);
						} else {
							print($line);
						}
					}
				} else if (isset($panel['table']) && is_array($panel['table'])) {
					$this->_table($panel['table']);
				} else if (isset($panel['html'])) {
					echo $panel['html'];
				}
				
				print('</div>');
				if (!empty($panel['post'])) echo $panel['post'];
			} else if (!is_null($panel)) {
				print($panel);
			}
		}
	}
/**
 * _actions method
 *
 * Additional lines after heading
 *
 * @param mixed $actions
 * @return void
 */
	private function _actions($actions) {
		if (!empty($actions['pre'])) echo $actions['pre'];
		if (!empty($actions['lines'])) {
			foreach ((array)$actions['lines'] as $name => $line) {
				if (is_array($line) && !empty($line['class'])) {
					$parameters = array();
					if (!empty($line['parameters'])) {
						$parameters = (array)$line['parameters'];
					}
					echo call_user_func_array(array($line['class'], $line['method']), $parameters);
				} else {
					echo $line;
				}
			}
		}
		if (!empty($actions['post'])) echo $actions['post'] . PHP_EOL;
	}
/**
 * _table method
 *
 * Display table from LilTable
 *
 * @param mixed $data
 * @return void
 */
	private function _table($data) {
		if (isset($data['pre'])) echo $data['pre'];
		echo '<table';
		if (!empty($data['element']['parameters'])) {
			foreach ($data['element']['parameters'] as $key => $param) {
				echo ' ' . $key . '="' . $param . '"';
			}
		}
		echo '>' . PHP_EOL;
		
		// display thead
		echo '<thead';
		if (!empty($data['element']['head']['parameters'])) {
			foreach ($data['element']['head']['parameters'] as $key => $param) {
				echo ' ' . $key . '="' . $param . '"';
			}
		}
		echo '>' . PHP_EOL;
		
		
		$default_parameters = array('class' => 'ui-toolbar ui-widget-header ui-corner-top');
		if (!empty($data['element']['head']['rows']))
		foreach ($data['element']['head']['rows'] as $row) {
			if (!isset($row['column'])) $row['column'] = 'th';
			echo '<tr';
			$params = array_merge($default_parameters, isset($row['parameters']) ? $row['parameters'] : array());
			foreach ($params as $key => $param) {
				echo ' ' . $key . '="' . $param . '"';
			}
			echo '>' . PHP_EOL;
			
			foreach ($row['columns'] as $col) {
				echo '<' . $row['column'];
				if (!empty($col['parameters']) && is_array($col['parameters'])) {
					foreach ($col['parameters'] as $key => $param) {
						echo ' ' . $key . '="' . $param . '"';
					}
				}
				echo '>';
				
				echo is_string($col) ? $col : $col['html'];
				echo '</' . $row['column'] . '>' . PHP_EOL;
			}
			
			echo '</tr>' . PHP_EOL;
		}
		echo '</thead>' . PHP_EOL;
		
		// display body
		if (!empty($data['element']['body']['rows'])) foreach ($data['element']['body']['rows'] as $row) {
			if ($row) {
				echo '<tr';
				if (!empty($row['parameters'])) {
					foreach ($row['parameters'] as $key => $param) {
						echo ' ' . $key . '="' . $param . '"';
					}
				}
				echo '>' . PHP_EOL;
				
				foreach ($row['columns'] as $col) {
					echo '<td';
					if (!empty($col['parameters']) && is_array($col['parameters'])) {
						foreach ($col['parameters'] as $key => $param) {
							echo ' ' . $key . '="' . $param . '"';
						}
					}
					echo '>';
					echo is_string($col) ? $col : $col['html'];
					echo '</td>' . PHP_EOL;
				}
				
				echo '</tr>' . PHP_EOL;
			}
		}
		
		// display tfoot
		if (!empty($data['element']['foot'])) {
			echo '<tfoot';
			if (!empty($data['element']['foot']['parameters'])) {
				foreach ($data['element']['foot']['parameters'] as $key => $param) {
					echo ' ' . $key . '="' . $param . '"';
				}
			}
			echo '>' . PHP_EOL;
			
			$default_parameters = array('class' => 'ui-toolbar ui-widget-header ui-corner-bl ui-corner-br');
			foreach ($data['element']['foot']['rows'] as $row) {
				if ($row) {
					echo '<tr';
					$params = array_merge(
						$default_parameters,
						isset($row['parameters']) ? (array)$row['parameters'] : array()
					);
					if (!empty($params)) {
						foreach ($params as $key => $param) {
							echo ' ' . $key . '="' . $param . '"';
						}
					}
					echo '>' . PHP_EOL;
					
					foreach ($row['columns'] as $col) {
						if (is_string($col)) {
							echo '<th>' . $col . '</th>' . PHP_EOL;
						} else {
							echo '<th';
							if (!empty($col['parameters'])) {
								foreach ($col['parameters'] as $key => $param) {
									echo ' ' . $key . '="' . $param . '"';
								}
							}
							echo '>';
							echo $col['html'];
							echo '</th>' . PHP_EOL;
						}
					}
					
					echo '</tr>' . PHP_EOL;
				}
			}
			echo '</tfoot>' . PHP_EOL;
		}
		
		echo '</table>' . PHP_EOL;
		if (isset($data['post'])) echo $data['post'] . PHP_EOL;
	}
}

/**
 * clean_pre function
 *
 * Callback function from regex which removes new lines
 *
 * @param mixed $matches
 * @return string
 */
function clean_pre($matches) {
	if ( is_array($matches) )
		$text = $matches[1] . $matches[2] . "</pre>";
	else
		$text = $matches;

	$text = str_replace('<br />', '', $text);
	$text = str_replace('<p>', "\n", $text);
	$text = str_replace('</p>', '', $text);

	return $text;
}